package opn.srn.command.load;

import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;

import opn.srn.command.load.parser.IParser;
import opn.srn.command.load.parser.RecordMetaData;
import opn.srn.command.load.parser.util.ParserUtil;
import opn.srn.grammar.recordformat.RecordFormatLexer;
import opn.srn.grammar.recordformat.RecordFormatParser;
import opn.srn.grammar.recordformat.RecordFormatParser.MemberContext;
import opn.srn.grammar.recordformat.RecordFormatParser.MembersContext;
import opn.srn.grammar.recordformat.RecordFormatParser.ParameterContext;
import opn.srn.grammar.recordformat.RecordFormatParser.ParametersContext;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.StringEscapeUtils;

public class FormatHelper {

	public static IParser load(String formatFilename) throws Exception {
		RecordFormatLexer lexer = new RecordFormatLexer(new ANTLRInputStream(new FileReader(formatFilename)));
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		RecordFormatParser p = new RecordFormatParser(tokens);
		p.setBuildParseTree(true);
		ParseTree tree = p.format();
		if (p.getNumberOfSyntaxErrors() != 0) {
			throw new Exception("Syntax error in format file " + formatFilename);
		}

		RecordMetaData metaData = parseRecord(tree);
		IParser parser = ParserUtil.createParser(metaData);
		return parser;
	}

	/*
	 * private static String getFileContent(String fileName) throws IOException { BufferedReader bufferedReader = null;
	 * 
	 * try { bufferedReader = new BufferedReader(new FileReader(fileName));
	 * 
	 * StringBuffer stringBuffer = new StringBuffer(); String line = null;
	 * 
	 * while ((line = bufferedReader.readLine()) != null) { stringBuffer.append(line).append("\n"); } return
	 * stringBuffer.toString(); } finally { bufferedReader.close(); } }
	 */

	private static RecordMetaData parseRecord(ParseTree tree) {
		RecordMetaData record = new RecordMetaData("record");
		record.setParameters(createParameters((ParametersContext) tree.getChild(1)));

		MembersContext membersContext = (MembersContext) tree.getChild(3);
		createMembers(membersContext, record);
		return record;
	}

	private static void createMembers(MembersContext membersContext, RecordMetaData group) {
		int noOfMembers = (membersContext.getChildCount() + 1) / 2;

		if (noOfMembers == 0) {
			return;
		}

		for (int index = 0; index < noOfMembers; index++) {
			group.addMember(createMember((MemberContext) (membersContext.getChild(index * 2))));
		}
	}

	private static RecordMetaData createMember(MemberContext memberContext) {
		String fieldName = memberContext.getChild(0).getChild(0).getText();

		RecordMetaData member = new RecordMetaData(fieldName);
		member.setParameters(createParameters((ParametersContext) memberContext.getChild(1)));

		int childCount = memberContext.getChildCount();
		if (childCount > 2) {
			createMembers((MembersContext) memberContext.getChild(3), member);
		}
		return member;
	}

	private static Map<String, String> createParameters(ParametersContext child) {
		int parametersCount = (child.getChildCount() - 2 + 1) / 2;

		if (parametersCount == 0) {
			return null;
		}

		Map<String, String> parameters = new HashMap<String, String>();
		for (int index = 0; index < parametersCount; index++) {
			addToParameter((ParameterContext) child.getChild(index * 2 + 1), parameters);
		}
		return parameters;
	}

	private static void addToParameter(ParameterContext child, Map<String, String> parameters) {
		String key = child.getChild(1).getChild(0).getText();
		TerminalNode valueNode = (TerminalNode) child.getChild(3).getChild(0);

		int valueType = valueNode.getSymbol().getType();
		String value = valueNode.getText().trim();
		if (valueType == RecordFormatParser.STRING) {
			value = value.substring(1, value.length() - 1);
		}

		parameters.put(key, StringEscapeUtils.unescapeHtml4(value));
	}
}
